package com.woniuxy.educationplatform.configuration;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.woniuxy.educationplatform.realm.LoginAndAuthorizationRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author unreleased
 * @date 2020/12/8 17:11
 */
@Configuration
public class ShiroConfiguration {

    /**
     * 配置缓存管理器
     * 作用：根据配置文件的内容，生成对应的缓存区间
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    /**
     * 用于thymeleaf模板使用shiro标签
     * 不加shiro标签不生效
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }


    /**
     * 配置Shiro的 会话支持管理器
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        //该会话 跟HttpSession没有任何关系
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //默认会话超时时间为：30分钟
        sessionManager.setGlobalSessionTimeout(1800000);
        //删除失效的Session会话
        sessionManager.setDeleteInvalidSessions(true);
        //设置认证时，是否需要使用Session
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

    /**
     * 配置Shiro过滤器
     * 该过滤器的作用：完成对页面请求的过滤
     * 并针对请求，配置对应的过滤规则
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //配置安全管理器（所有的请求，需要经过安全管理器）
        factoryBean.setSecurityManager(securityManager());
        //配置登录路径，成功路径，未授权路径
        //登录路径
        factoryBean.setLoginUrl("/");
        //登录成功之后路径
        factoryBean.setSuccessUrl("/index");
        //未授权路径
        factoryBean.setUnauthorizedUrl("/unauthorized");

        //配置Shiro的过滤器链
        Map<String,String> filters = new LinkedHashMap<>();
        //anon 表示匿名  无需认证，直接操作
        //登录页面   匿名方式
        filters.put("/","anon");
        //登录请求    匿名方式
        filters.put("/system/login","anon");
        //静态资源    匿名方式
        filters.put("/user/admin","roles[superadmin]");
        filters.put("/user/role","roles[superadmin]");

        filters.put("/static/**","anon");
        //authc 一定要认证
        //其他的，暂定为：需要认证
        //filters.put("/**","authc");
        filters.put("/**","user");



        //将过滤规则，设置给Shiro工厂
        factoryBean.setFilterChainDefinitionMap(filters);

        return factoryBean;
    }

    /**
     * 配置 身份匹配器
     * 作用：判断token 信息和 info 信息是否一致
     * @return
     */
    @Bean
    public HashedCredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //说密码的加密方式为MD5 (不可逆的加密方式，只能加密，不能解密)
        credentialsMatcher.setHashAlgorithmName("MD5");
        //针对MD5加密的信息，再加密1024次
        credentialsMatcher.setHashIterations(1024);

        return credentialsMatcher;
    }


    /**
     * 安全管理器
     * 相当于SpringMVC框架的DispatherServlet
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //告诉安全管理器，认证和授权的对象是哪个
        securityManager.setRealm(loginAndAuthorizationRealm());
        //如果有多个认证和授权对象，请使用
        //securityManager.setRealms(集合);

        securityManager.setSessionManager(sessionManager());

        //将缓存管理器，交给安全管理器
        securityManager.setCacheManager(ehCacheManager());

        //将“记住我管理器”配置给安全管理器
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 配置shiro框架中，用来完成身份认证，授权的域对象
     * @return
     */
    @Bean
    public LoginAndAuthorizationRealm loginAndAuthorizationRealm(){

        LoginAndAuthorizationRealm realm = new LoginAndAuthorizationRealm();
        //给Realm 中配置 身份对比规则
        realm.setCredentialsMatcher(credentialsMatcher());

        //让realm支持缓存
        realm.setCachingEnabled(true);
        //配置认证信息，需要使用缓存
        realm.setAuthenticationCachingEnabled(true);
        realm.setAuthenticationCacheName("authenticationCache");
        //配置授权信息，需要使用缓存
        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("authorizationCache");
        return realm;
    }


    /**
     * 管理Subject主体对象，生命周期的组件
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 以下2个配置，是用来开启Shiro的注解支持,并提供AOP代理实现
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    /**
     * 配合上述的配置类，完成对使用了@RequiresPermissions,@RequiresRoles进行授权通知
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 配置Cookie管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        byte[] cipherKey = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");
        rememberMeManager.setCipherKey(cipherKey);
        rememberMeManager.setCookie(rememberMeCookie());
        return rememberMeManager;
    }
    /**
     * 配置让Cookie实现 记住我 的功能
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        /*
         * cookie 的最大失效时间 7天，单位：秒 maxAge=-1 表示关闭浏览器cookie失效
         */
        cookie.setMaxAge(1800);
        cookie.setPath("/");
        return cookie;
    }

}
